import java.io.IOException;

import javax.xml.bind.JAXBException;

public class Improvise implements Runnable {

	static Object[] noteArray = new Object[2];
	public static int totalLikelihood=0;

	public void run() {
//		System.out.println("Main.score.head = " + Main.score.head);
		if (Main.score.head) { // play original line
//			System.out.println("TIME: " + Main.score.barPointer);
			int[][] notes = new int[2][0];
			int scoreDuration = 0;
			for (int i = 0; i < Main.score.changes[2].length; i++) {
				scoreDuration = scoreDuration + Main.score.changes[2][i];
			}
			int numBars = 10 * scoreDuration / Main.stats.TPB;
			if (Main.score.barPointer % numBars < Main.score.pitch.length) {
				notes[0] = Main.score.pitch[Main.score.barPointer % numBars];
				notes[1] = Main.score.onset[Main.score.barPointer % numBars];

//				System.out.println("INITIAL WAIT: " + notes[1][0]);
				@SuppressWarnings("unused")
				int length = 0;
				if (notes[0].length < notes[1].length) {
					length = notes[0].length;
				} else {
					length = notes[1].length;
				}

				for (int i = 0; i < notes[0].length; i++) {
					notes[0][i] = notes[0][i];
				}
			}

			if (Main.score.trans)
			{
				notes = transform(notes);
			}
			Main.pchOut=AltMath.incMatrix(Main.pchOut);
			Main.rhyOut=AltMath.incMatrix(Main.rhyOut);
			Main.chdOut[0]=AltMath.incArray(Main.chdOut[0]);
			Main.chdOut[1]=AltMath.incArray(Main.chdOut[1]);
			Main.chdOut[2]=AltMath.incArray(Main.chdOut[2]);
			Main.pchOut = new int[Main.pchOut.length + 1][];
			Main.rhyOut = new int[Main.rhyOut.length + 1][];
//			System.out.println("Main.chdOut[0].length = "+Main.chdOut[0].length);
			Main.chdOut[0][Main.chdOut[0].length - 1] = Main.score.changes[0][(Main.score.chordPointer)
					% Main.score.changes[0].length];
			Main.chdOut[1][Main.chdOut[1].length - 1] = Main.score.changes[1][(Main.score.chordPointer)
			                                              					% Main.score.changes[1].length];
			Main.chdOut[2][Main.chdOut[2].length - 1] = Main.score.changes[2][(Main.score.chordPointer)
			                                              					% Main.score.changes[2].length];
			if (notes!=null) {
				Main.pchOut[Main.pchOut.length - 1] = notes[0];
				Main.rhyOut[Main.rhyOut.length - 1] = notes[1];
			} else {
				Main.pchOut[Main.pchOut.length - 1] = null;
				Main.rhyOut[Main.rhyOut.length - 1] = null;
			}
			Main.barOutCounter++;
			if (notes != null) {
				if (notes[0].length != 0 && notes[1].length != 0) {
					// System.out.println("------ rows = " + notes.length
							// + " col = " + notes[1].length);
//					waitSym(notes[1][0]); // wait until first onset
					for (int i = 0; i < notes[1].length; i++) {
						if (notes[0][i] != 0) {

							if (i < (notes[1].length - 1)
									&& (notes[1][i + 1] - notes[1][i]) >= 0) {
								if (notes[1][i + 1] - notes[1][i] < 5) {
									// System.out.println("?????????????????");
								} else {
									noteOut(notes[0][i], 100, notes[1][i]);
								}
							} else if (notes[1].length > 1 && i > 0) {
								if ((notes[1][i] - notes[1][i - 1]) > 5) {
									noteOut(notes[0][i], 100, notes[1][i]);
								}
							} else {
								noteOut(notes[0][i], 100, notes[1][i]);
							}
						}
					}
				}
			}

		} else {
				improvise();
		}
		Main.score.improvDone=true;
	}

	public static void improvise() {
		// System.out.println("TEST -------------");
		int[][] notes = improvNotes();
		int length = 0;
		if (notes!=null) {
			if (notes[0]!=null && notes[1]!=null)
			{
				if (notes[0].length < notes[1].length) {
					length = notes[0].length;
				} else {
					length = notes[1].length;
				}
				if (notes[1].length > 0) {
	//				waitSym(notes[1][0]); // wait until first onset
					for (int i = 0; i < length; i++) {
						if (notes[0][i] != 0) {
							if (i < notes[1].length) {
	//							waitSym(notes[1][i + 1] - notes[1][i]);

									noteOut(notes[0][i], 100, notes[1][i]);
							}
						}
					}
				}
			}
		}
		
		Main.score.improvDone=true;
	}

	public static int findBar(int chordIndex) {
		int ticks = 0;
		for (int i = 0; i < chordIndex; i++) {
			ticks = ticks + Main.stats.chordDurations[i];
		}
		int bar = 10 * ticks / Main.stats.TPB;
		return bar;
	}

	public static void loadArtist(String nameFormatted) {
		try {
			Main.xml.newArtist(nameFormatted);
			boolean exists = Main.xml.readArtist(nameFormatted);
			if (exists) {
				Main.stats.volume = XML.getVolume(nameFormatted);
				Main.stats.pitch = XML.getPitch(nameFormatted);
				Main.stats.duration = XML.getDuration(nameFormatted);
				Main.stats.onset = XML.getOnsets(nameFormatted);
				Main.stats.chordRoots = XML.getChordRoots(nameFormatted);
				Main.stats.chordQualities = XML
						.getChordQualities(nameFormatted);
				Main.stats.chordDurations = XML
						.getChordDurations(nameFormatted);
			} else {
				// System.out.println("***** ARTIST NOT FOUND -- " + nameFormatted);
			}
		} catch (JAXBException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void loadHuman() {
		Main.stats.volume = Main.velIn;
		Main.stats.pitch = Main.pchIn;
		Main.stats.onset = Main.rhyIn;
		
		Main.stats.chordRoots = Main.chdIn[0];
		Main.stats.chordQualities = Main.chdIn[1];
		Main.stats.chordDurations = Main.chdIn[2];
	}

	public static int[] findOptions(int chord) {
		int[] options = null; // pick arrays that match current chord root
		if (Math.random() < Main.isHuman) {
			int count = 0;
			loadHuman();
			int numOptions = 0;
			int[] tempOptions = null;
			for (int i = 0; i < Main.stats.chordRoots.length; i++) {
				if (chord == Main.stats.chordRoots[i]
						&& i <= Main.stats.pitch.length) {
					if (i<Main.pchIn.length) {
						numOptions++;
						if (options != null) {
							tempOptions = options;
							options = new int[options.length + 1];
							System.arraycopy(tempOptions, 0, options, 0,
									tempOptions.length);
							options[options.length - 1] = findBar(i);
						} else {
							options = new int[1];
							options[0] = findBar(i);
						}
					}
				}
			}
			count++;
		}
		if (options == null) {
			if (Main.stats.artists.length > 0) // select artist
			{
				int selectedArtist = (int) (Math.random() * Main.stats.artists.length);
				Main.stats.selectedArtist = selectedArtist;
				int count = 0;
				while (options == null && count < Main.stats.artists.length) {
					loadArtist(Main.stats.artists[(selectedArtist + count)
							% Main.stats.artists.length]);
					int numOptions = 0;
					int[] tempOptions = null;
					for (int i = 0; i < Main.stats.chordRoots.length; i++) {
						if (chord == Main.stats.chordRoots[i]
								&& i < Main.stats.pitch.length) {
							numOptions++;
							if (options != null) {
								tempOptions = options;
								options = new int[options.length + 1];
								System.arraycopy(tempOptions, 0, options, 0,
										tempOptions.length);
								options[options.length - 1] = findBar(i);
							} else {
								options = new int[1];
								options[0] = findBar(i);
							}
						}
					}
					count++;
				}
			} else {
				int numOptions = 0;
				int[] tempOptions = null;
				for (int i = 0; i < Main.stats.chordRoots.length; i++) {
					if (chord == Main.stats.chordRoots[i]
							&& i <= Main.stats.pitch.length) {
						numOptions++;
						if (options != null) {
							tempOptions = options;
							options = new int[options.length + 1];
							System.arraycopy(tempOptions, 0, options, 0,
									tempOptions.length);
							options[options.length - 1] = findBar(i);
						} else {
							options = new int[1];
							options[0] = findBar(i);
						}
					}
				}
			}
			// if (options == null) { // no matches found, find similar chords
			// int[] chordParallels = null;
			// if (Main.score.isMinor) {
			// chordParallels = Constants.chordMinorParallels[chord];
			// } else {
			// chordParallels = Constants.chordParallels[chord];
			// }
			// for (int j = 0; j < chordParallels.length; j++) {
			// for (int i = 0; i < Main.stats.chordRoots.length; i++) {
			// if (chordParallels[j] == Main.stats.chordRoots[i]) {
			// numOptions++;
			// if (options != null) {
			// tempOptions = options;
			// options = new int[options.length + 1];
			// System.arraycopy(tempOptions, 0, options, 0,
			// tempOptions.length);
			// options[options.length - 1] = findBar(i);
			// } else {
			// options = new int[1];
			// options[0] = findBar(i);
			// }
			// }
			// }
			// }
			// }
		}
		return options;
	}

	@SuppressWarnings("null")
	public static int[][] findOptionsOnsets(int chord) {
		// System.out.println("findOptionsOnsets TEST 0");
		int[][] options = new int[2][0]; // pick arrays that match current chord root
		if (Math.random() < Main.isHuman) {		
			loadHuman();
			int numOptions = 0;
			totalLikelihood = 0;
			for (int i = 0; i < Main.chdIn.length; i++) {
				if (i < Main.rhyIn.length) {
					numOptions++;
					totalLikelihood+=Main.rhyIn[i].length;
					options[0] = AltMath.incArray(options[0]);
					options[1] = AltMath.incArray(options[1]);
					options[1][options[1].length-1]=Main.rhyIn[i].length;
					options[0][options[0].length - 1] = findBar(i);
				}
			}
		}
		if (options[0].length == 0) {

			// System.out.println("findOptionsOnsets TEST 1");
			if (Main.stats.artists.length > 0) // select artist
			{
				// System.out.println("findOptionsOnsets TEST 2");
				int selectedArtist = (int) (Math.random() * Main.stats.artists.length);
				Main.stats.selectedArtist = selectedArtist;
				int count = 0;
				// System.out.println("findOptionsOnsets TEST 3");
				while (options[0].length == 0 && count < Main.stats.artists.length) {
					// System.out.println("findOptionsOnsets TEST 4");

					loadArtist(Main.stats.artists[(selectedArtist + count)
							% Main.stats.artists.length]);
					int numOptions = 0;
					totalLikelihood = 0;
					for (int i = 0; i < Main.stats.chordRoots.length; i++) {
						if (i < Main.stats.onset.length) {
							numOptions++;
							totalLikelihood+=Main.stats.onset[i].length;
							options[0] = AltMath.incArray(options[0]);
							options[1] = AltMath.incArray(options[1]);
							options[1][options[1].length-1]=Main.stats.onset[i].length;
							options[0][options[0].length - 1] = findBar(i);
						}
					}					
					count++;
				}
			} else {
				int numOptions = 0;
				totalLikelihood = 0;
				for (int i = 0; i < Main.stats.chordRoots.length; i++) {
					if (i < Main.stats.onset.length) {
						numOptions++;
						totalLikelihood+=Main.stats.onset[i].length;
						options[0] = AltMath.incArray(options[0]);
						options[1] = AltMath.incArray(options[1]);
						options[1][options[1].length-1]=Main.stats.onset[i].length;
						options[0][options[0].length - 1] = findBar(i);
					}
				}		
			}
		}
	return options;
	}
	
	public static int[][] transform(int[][] notes) {
		if (notes[0] != null && notes[1] != null) {
			if (notes[0].length > 0) {
				int[][] tempNotes = new int[2][notes[0].length - 1];
				System.arraycopy(notes[0], 0, tempNotes[0], 0,
						notes[0].length - 1);
				System.arraycopy(notes[1], 0, tempNotes[1], 0,
						notes[1].length - 1);

				notes = tempNotes;
			}
			if (notes[0].length == 0 || notes[1].length == 0) {
				notes = null;
			} else {
				// int choice = (int)(Math.random()*3);
				// switch(choice) {
				// case (0):
				// notes = offset(notes);
				// notes = rhySuper3(notes); // play melody segment across
				// enough onsets then add the rest as improv pitches
				//	
				// break;
				// case (1):
				// notes = rhySuper3(notes); // play melody segment across
				// enough onsets then add the rest as improv pitches
				// break;
				// case (2):
				// break;
				// case (3):
				// notes = rhySuper2(notes); // keep repeating melody segment
				// across all onsets of bar
				// break;
				// case (4):
				// break;
				// }
				// System.out.println("************* case = "+choice);
//				notes = offset(notes);
				// notes = retrograde(notes); // retrograde the pitch sequence
				// per bar
				// notes = addImprov(notes);
				if (Math.random()>.5) {
					notes = retrograde(notes);									
				}
				if (Math.random()>.5) {
					notes = offset(notes);									
				}
				if (Math.random()>.7) {
					notes = passingTone(notes);
				}
				if (Math.random()>.2) {
					notes = rhySuper3(notes); // play melody segment across enough
					// onsets then add the rest as
					// improv pitches
				}
				

				if (notes != null) {
					for (int i = 0; i < notes[1].length; i++) {
						// System.out.println("NEW NOTE 1:" + notes[1][i]);
						// System.out.println("NEW NOTE: " + notes[0][i]);
					}
				}
			}
		}

		if (notes == null) {
			notes = improvNotes();
		}

		return notes;
	}

	public static int[][] retrograde(int[][] notes) {
		int[] tempPchSeq = new int[notes[0].length];
		for (int i = 0; i < notes[0].length; i++) {
			tempPchSeq[i] = notes[0][notes[0].length - 1 - i];
		}
		notes[0] = tempPchSeq;
		return notes;
	}
	
	public static int[][] passingTone(int[][] notes) {
		notes=Transformations.passingTones(notes);
		return notes;
	}
	

	public static int[][] offset(int[][] notes) { // offset one of the notes by an
											// eighth
		if (notes[0] != null && notes[1] != null) {
			int[] options = null;
			for (int i = 0; i < notes[1].length; i++) { // for all of the notes
				if (notes[1][i] < Main.stats.TPB - 240) { // make sure the
															// offset won't land
															// the note in the
															// next bar
					if (i < notes[1].length - 1) { // make sure the offset won't
													// interfere with the 'next'
													// note
						if (notes[1][i] < (notes[1][i + 1] + 240)) {
							if (options == null) {
								options = new int[1];
								options[0] = i;
							} else {
								int[] optionsTemp = options;
								options = new int[options.length + 1];
								System.arraycopy(optionsTemp, 0, options, 0,
										optionsTemp.length);
							}
						}
					} else {
						if (options == null) {
							options = new int[1];
							options[0] = i;
						} else {
							int[] optionsTemp = options;
							options = new int[options.length + 1];
							System.arraycopy(optionsTemp, 0, options, 0,
									optionsTemp.length);
						}
					}
				}
			}

			if (options != null) {
				int selectedOption = options[(int) (Math.random() * options.length)];
				notes[1][selectedOption] = notes[1][selectedOption] + 240;
			}
		}
		return notes;
	}

	public int[][] pchSuper1(int[][] notes) {
		int[] tempNotes = improvPch(notes[1].length);
		if (tempNotes != null) {
			notes[0] = tempNotes;
		}
		if (notes[0].length < notes[1].length) {
			int[][] tempNotes2 = new int[2][notes[0].length - 1];
			for (int i = 0; i < tempNotes2[0].length; i++) {
				tempNotes2[0][i] = notes[0][i];
				tempNotes2[1][i] = notes[1][i];
			}
			notes = tempNotes2;
		}
		return notes;
	}

	public int[] improvPch(int length) {
		int[] pitches = new int[1];

		@SuppressWarnings("unused")
		int improvNotes[][] = new int[2][];
		int chord = Main.score.changes[0][(Main.score.chordPointer)
				% Main.score.changes[0].length];
		// System.out.println("Search to match chord " + chord);
		int[] options = findOptions(chord);
		if (options != null) // create improvisation
		{
			// System.out.println("options length = "+options.length);
			int selectedOption = options[(int) (Math.random() * options.length)];
			int[] pitch = Main.stats.pitch[selectedOption];
			int index = selectedOption + 1;
			while (index < length || pitch.length < length
					&& index < Main.stats.pitch.length) {
				int[] newArray = Main.stats.pitch[index];
				for (int i = 0; i < newArray.length; i++) {
					pitch[pitch.length - 1] = newArray[i];
					int[] temp = pitch;
					pitch = new int[pitch.length + 1];
					System.arraycopy(temp, 0, pitch, 0, temp.length);
				}
				index++;
			}
			pitches = pitch;
		} else {
			pitches = null;
		}

		return pitches;
	}

	public static int[][] improvNotes() {
		int improvNotes[][] = new int[2][0];
		int chord = Main.score.changes[0][(Main.score.chordPointer)
				% Main.score.changes[0].length];
		// System.out.println("Search to match chord " + chord);
		int[][] options = findOptionsOnsets(chord);
		int selectedOption = 0;
		if (Math.random()<Main.isHuman) { // if not human use pure chance on bar selection
			selectedOption = options[0][(int) (Math.random() * options[0].length)];
		}
		else { // if human, weight the more dense bars higher
			int optionRandIndex = (int) (Math.random() * totalLikelihood);
			int index=0;
			int count=0;
			while (count<optionRandIndex&&index<options[0].length) {
				count+=options[1][index];
				index++;
			}
			if (index>0)
			{
				index--;
			}
			selectedOption = options[0][index];
		}
		int[] onsets = Main.stats.onset[selectedOption];
		int[] pchOptions = findOptions(chord);
		if (options != null && pchOptions != null) // create improvisation
		{
			selectedOption = pchOptions[(int) (Math.random() * pchOptions.length)]; // select
			// another
			// option
			int[] pitch = Main.stats.pitch[selectedOption];
			int index = selectedOption + 1;
			while ((pitch.length < onsets.length)
					&& index < Main.stats.pitch.length) {
				for (int i = 0; i < Main.stats.pitch[index].length; i++) {
					pitch = AltMath.incArray(pitch);
					pitch[pitch.length - 1] = Main.stats.pitch[index][i];
				}
				index++;
			}

//			find lesser length of pitch and onset
			int length=0;
			if (onsets.length<=pitch.length) {
				length=onsets.length;
			} else {
				System.out.println("******************PITCH LENGTH IS LESS********************");
				length=pitch.length;
			}
			
			for (int i=0;i<length;i++) {
				improvNotes[0]=AltMath.incArray(improvNotes[0]);
				improvNotes[0][i]=pitch[i];
				improvNotes[1]=AltMath.incArray(improvNotes[1]);
				improvNotes[1][i]=onsets[i];
			}

			// find nearest octave
			int octaveDisp = 0;
			if (improvNotes[0].length > 0 && Main.lastPchOut > 0) {
				System.out.println("****************** improvNotes[0][0] % 12 = " +improvNotes[0][0] % 12+ " Main.lastPchOut = "+Main.lastPchOut+" ********************");

				octaveDisp = Generation.nearestOctave(improvNotes[0][0] % 12,
						Main.lastPchOut);
				if (octaveDisp > 12) {
					octaveDisp = 12;
				}
				if (octaveDisp < -12) {
					octaveDisp = -12;
				}
			}

			for (int i = 0; i < length; i++) {
				improvNotes[0][i] = improvNotes[0][i] + octaveDisp;
				System.out.println("-*-*-*-*-* to be generated note: "+improvNotes[0][i]+" "+improvNotes[1][i]);
			}
		} else {
			improvNotes = null;
		}
		
		return improvNotes;
	}

	public int[][] rhySuper1(int[][] notes) {
		int[][] improvNotes = improvNotes();
		int[][] newNotes = new int[2][1];
		if (improvNotes != null && notes[0].length > 0) {
			// System.out.println("--------notes[0].length: " + notes[0].length);
			if (improvNotes[1].length > 1) {
				for (int i = 0; i < notes[0].length; i++) {
					if (i > 0) {
						int[] temp = newNotes[0];
						newNotes[0] = new int[newNotes[0].length + 1];
						System.arraycopy(temp, 0, newNotes[0], 0, temp.length);
						temp = newNotes[1];
						newNotes[1] = new int[newNotes[1].length + 1];
						System.arraycopy(temp, 0, newNotes[1], 0, temp.length);
					} else {
						newNotes[0][0] = notes[0][0];
						newNotes[1][0] = improvNotes[1][0];
					}
					newNotes[0][i] = notes[0][i % notes[0].length];
					newNotes[1][i] = improvNotes[1][i];
				}
				notes = newNotes;
			}
		}
		return notes;
	}

	public int[][] rhySuper2(int[][] notes) {
		int[][] improvNotes = improvNotes();
		int[][] newNotes = new int[2][1];
		if (improvNotes != null && notes[0].length > 0) {
			// System.out.println("--------notes[0].length: " + notes[0].length);
			if (improvNotes[1].length > 1) {
				for (int i = 0; i < improvNotes[1].length; i++) {
					if (i > 0) {
						int[] temp = newNotes[0];
						newNotes[0] = new int[newNotes[0].length + 1];
						System.arraycopy(temp, 0, newNotes[0], 0, temp.length);
						temp = newNotes[1];
						newNotes[1] = new int[newNotes[1].length + 1];
						System.arraycopy(temp, 0, newNotes[1], 0, temp.length);
					} else {
						newNotes[0][0] = notes[0][0];
						newNotes[1][0] = improvNotes[1][0];
					}
					newNotes[0][i] = notes[0][i % notes[0].length];
					newNotes[1][i] = improvNotes[1][i];
				}
				notes = newNotes;
			}
		}
		return notes;
	}

	@SuppressWarnings("null")
	public static int[][] rhySuper3(int[][] notes) {
		int[][] improvNotes = improvNotes();
		int[][] newNotes = new int[2][1];

		if (improvNotes != null && notes[0].length > 0) {
			int octaveDisp = 0;
			// find nearest octave
			if (improvNotes[0].length > 0) {
				octaveDisp = Generation.nearestAbs(improvNotes[0][0] % 12,
						notes[0][notes[0].length - 1])
						- improvNotes[0][0];
			}

			int length = 0;
			if (notes[0].length < improvNotes[1].length) {
				length = notes[0].length;
			} else {
				length = improvNotes[1].length;
			}
			// System.out.println("--------notes[0].length: " + notes[0].length);
			if (improvNotes[1].length > 1) {
				for (int i = 0; i < length; i++) {
					if (i > 0) {
						int[] temp = newNotes[0];
						newNotes[0] = new int[newNotes[0].length + 1];
						System.arraycopy(temp, 0, newNotes[0], 0, temp.length);
						temp = newNotes[1];
						newNotes[1] = new int[newNotes[1].length + 1];
						System.arraycopy(temp, 0, newNotes[1], 0, temp.length);
						newNotes[0][i] = notes[0][i % notes[0].length];
//						System.out
//								.println("newNotes[1][i] = " + newNotes[1][i]);
//						System.out.println("improvNotes[1][i] "
//								+ improvNotes[1][i]);
						newNotes[1][i] = improvNotes[1][i];
					} else {
						newNotes[0][0] = notes[0][0];
						newNotes[1][0] = improvNotes[1][0];
					}

				}
				System.out.println("improvNotes[0].length = "+improvNotes[0].length);
				System.out.println("improvNotes[1].length = "+improvNotes[1].length);
				for (int i = notes[0].length; i < improvNotes[0].length; i++) {
					if (newNotes != null) {
						int[] temp = newNotes[0];
						newNotes[0] = new int[newNotes[0].length + 1];
						System.arraycopy(temp, 0, newNotes[0], 0, temp.length);
						temp = newNotes[1];
						newNotes[1] = new int[newNotes[1].length + 1];
						System.arraycopy(temp, 0, newNotes[1], 0, temp.length);
						newNotes[0][i] = improvNotes[0][i] + octaveDisp;
						newNotes[1][i] = improvNotes[1][i];
					} else {
						newNotes[0][0] = improvNotes[0][0] + octaveDisp;
						newNotes[1][0] = improvNotes[1][0];
					}
				}
				notes = newNotes;
			}
		}
		return notes;
	}

	public int[][] addImprov(int[][] notes) {

		int chord = Main.score.changes[0][(Main.score.chordPointer)
				% Main.score.changes[0].length];
		// System.out.println("Search to match chord " + chord);
		int[] options = findOptions(chord);
		if (options != null) // create improvisation
		{
			// System.out.println("options length = "+options.length);
			int selectedOption = options[(int) (Math.random() * options.length)];
//			System.out.println("Main.ONSETS LENGTH = "
//					+ Main.stats.onset.length);
//			System.out.println("selected option  = " + selectedOption);
			int[] onsets = Main.stats.onset[selectedOption];
			selectedOption = options[(int) (Math.random() * options.length)]; // select
			// another
			// option
//			System.out
//					.println("Main.pitch LENGTH = " + Main.stats.pitch.length);
//			System.out.println("selected option  = " + selectedOption);
			int[] pitch = Main.stats.pitch[selectedOption];
			int index = selectedOption + 1;
			while (index < onsets.length || pitch.length < onsets.length
					&& index < Main.stats.pitch.length) {
				int[] newArray = Main.stats.pitch[index];
				for (int i = 0; i < newArray.length; i++) {
					pitch[pitch.length - 1] = newArray[i];
					int[] temp = pitch;
					pitch = new int[pitch.length + 1];
					System.arraycopy(temp, 0, pitch, 0, temp.length);
				}
				index++;
			}

			if (onsets.length - 1 > 0) {
				int[][] tempNotes2 = new int[2][onsets.length - 1];
				System.arraycopy(pitch, 0, tempNotes2[0], 0, onsets.length - 1);
				System
						.arraycopy(onsets, 0, tempNotes2[1], 0,
								onsets.length - 1);
				pitch = tempNotes2[0];
				onsets = tempNotes2[1];
			}

			int[] addNotes = null;
			for (int i = 0; i < onsets.length; i++) {
				boolean match = false;
				for (int j = 0; j < notes[1].length; j++) {
					if (onsets[i] == notes[1][j]) {
						match = true;
					}
				}
				if (!match) {
					if (addNotes == null) {
						addNotes = new int[1];
						addNotes[0] = i;
					} else {
						int[] tempAddNotes = addNotes;
						addNotes = new int[addNotes.length + 1];
						System.arraycopy(tempAddNotes, 0, addNotes, 0,
								tempAddNotes.length);
						addNotes[addNotes.length - 1] = i;
					}
				}
			}
			if (notes[0] != null && addNotes != null) {
				int[][] tempNotes = new int[2][notes[0].length
						+ addNotes.length];
				int addNotesPointer = 0;
				int notesPointer = 0;
				if (addNotes != null) {
					for (int i = 0; i < tempNotes[0].length; i++) {
						if (addNotesPointer < addNotes.length
								&& notesPointer < notes[0].length) {
							if (onsets[addNotes[addNotesPointer]] < notes[1][notesPointer]) {
								tempNotes[1][i] = onsets[addNotes[addNotesPointer]];
								tempNotes[0][i] = pitch[addNotes[addNotesPointer]];
//								System.out.println("adding improv note: "
//										+ tempNotes[0][i] + " "
//										+ tempNotes[1][i]);
								addNotesPointer++;
							} else {
								tempNotes[0][i] = notes[0][notesPointer];
								tempNotes[1][i] = notes[1][notesPointer];
//								System.out.println("adding original note: "
//										+ tempNotes[0][i] + " "
//										+ tempNotes[1][i]);
								notesPointer++;
							}
						} else if (addNotesPointer < addNotes.length) {
							tempNotes[1][i] = onsets[addNotes[addNotesPointer]];
							tempNotes[0][i] = pitch[addNotes[addNotesPointer]];
//							System.out.println("adding improv note: "
//									+ tempNotes[0][i] + " " + tempNotes[1][i]);
							addNotesPointer++;
						} else {
							tempNotes[0][i] = notes[0][notesPointer];
							tempNotes[1][i] = notes[1][notesPointer];
//							System.out.println("adding original note: "
//									+ tempNotes[0][i] + " " + tempNotes[1][i]);
							notesPointer++;
						}
					}
					notes = tempNotes;
				}
			} else if (notes == null) {
				notes = new int[2][];
				notes[0] = pitch;
				notes[1] = onsets;
			}
		}
		return notes;
	}

	public static void noteOut(int note, int vel, int onset) {
		vel = 30;
		Main.score.improvDone=true;
//		octave transposition
		Main.lastPchOut = note;
		note = note + Main.score.key + 24;
		if (note <= 68) {
			note = note % 12;
			note = note + 72;
		}
		if (note > 95) {
			note = note % 12;
			note = note + 84;
		}
		// add note to notes out


		boolean bound = false;
		if (Main.noteOut[2]==null) {
			Main.noteOut = new int[3][1];
			Main.noteOut[0][0]=note;
			Main.noteOut[1][0]=vel;
			Main.noteOut[2][0]=onset;
		}
		else {		
			int index=Main.noteOut[2].length-1;
			while (!bound&&index>=0)
			{
				if  (onset>=Main.noteOut[2][index]) {
					bound = true;
				}
				else {
					index--;
				}
			}
			int[][] tempOut = new int[3][Main.noteOut[2].length+1];
			int index2=0;
			while (index2<=index)
			{
				tempOut[0][index2]=Main.noteOut[0][index2];
				tempOut[1][index2]=Main.noteOut[1][index2];
				tempOut[2][index2]=Main.noteOut[2][index2];
				index2++;
			}
			tempOut[0][index2]=note;
			tempOut[1][index2]=vel;
			tempOut[2][index2]=onset;
			while (index2<tempOut[2].length-1)
			{
				tempOut[0][index2+1]=Main.noteOut[0][index2];
				tempOut[1][index2+1]=Main.noteOut[1][index2];
				tempOut[2][index2+1]=Main.noteOut[2][index2];
				index2++;
			}
			Main.noteOut=tempOut;
		}

		System.out.println("generated improv note:" + (note + Main.score.key)
				+ " " + onset);
	}

	public void waitSym(double wait) // wait in milliseconds
	{
		long waitMS = (long) (wait * 60000 / (Main.bpm * 480));
		try {
			Thread.sleep(waitMS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
